﻿using System;
using System.Diagnostics;
using System.Net;

using Hammock;
using Hammock.Authentication;
using Hammock.Authentication.Basic;
using Hammock.Serialization;
using Hammock.Web;

namespace TMWS
{
	using Utilities;

	/// <summary>
	/// Service base
	/// </summary>
	public abstract class ServiceBase
	{
		#region Fields

		/// <summary>
		/// The _credentials
		/// </summary>
		private IWebCredentials _credentials;

		/// <summary>
		/// The _client
		/// </summary>
		private RestClient _client;

		/// <summary>
		/// The _request
		/// </summary>
		private RestRequest _request;

		/// <summary>
		/// The _requestBase
		/// </summary>
		private string _requestBase = "https://api.maniastudio.com/";

		/// <summary>
		/// The _username
		/// </summary>
		private string _username;

		/// <summary>
		/// The _password
		/// </summary>
		private string _password;

		/// <summary>
		/// The _userLogging
		/// </summary>
		private bool _useLogging;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="WebClient" />
		/// <param name="apiUsername">Your API username</param>
		/// <param name="apiPassword">Your API password</param>
		/// </summary>
		protected ServiceBase(string apiUsername, string apiPassword)
			: this(apiUsername, apiPassword, false)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="WebClient" />
		/// <param name="apiUsername">Your API username</param>
		/// <param name="apiPassword">Your API password</param>
		/// <param name="enableLogging">When true, the client will log any exceptions and all responses</param>
		/// <param name="logDirectory">The folder where log files should be stored</param>
		/// </summary>
		protected ServiceBase(string apiUsername, string apiPassword, bool enableLogging, string logDirectory = @"C:\TMWS.NET Logs\")
		{
			this._username = apiUsername;
			this._password = apiPassword;
			this._useLogging = enableLogging;

			// Setup credentials, we'll be using basic auth for now
			this._credentials = new BasicAuthCredentials
			{
				Username = apiUsername,
				Password = apiPassword
			};

			var serializer = new HammockDataContractJsonSerializer(); // Hammock JSON serializer

			// Create a new REST client
			this._client = new RestClient
			{
				Authority = this._requestBase,
				Serializer = serializer,
				Deserializer = serializer,
				Timeout = TimeSpan.FromSeconds(10)
			};

			this._client.AddHeader("User-Agent", "TMWS.NET"); // Set user agent
			this._client.AddHeader("Accept", "application/json"); // Set accept type
			this._client.AddHeader("Content-Type", "application/json"); // Set content type

			// Create a new REST request
			this._request = new RestRequest
			{
				Credentials = this._credentials
			};

			Log.LogDirectory = (!String.IsNullOrEmpty(logDirectory) ? logDirectory : @"C:\TMWS.NET Logs\"); // Set log directory
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Executes a request against the specified resource with the specified method
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <param name="resource">Resource location</param>
		/// <param name="method">HTTP method</param>
		/// <returns>Content entity</returns>
		protected T Execute<T>(string resource, WebMethod method, object data = null) where T : class
		{
			// Accept all certificates
			ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; });

			this._request.Path = resource; // Resource
			this._request.Method = method; // HTTP method

			if (method != WebMethod.Get)
			{
				// Anything other than GET does not need to have the
				// response deserialized, therefore we do not need the deserializer
				this.Client.Deserializer = null;
			}

			try
			{
				// Using POST or PUT
				if (method == WebMethod.Post || method == WebMethod.Put)
				{
					// Do we have data?
					if (data == null)
					{
						// No, throw an exception
						throw new ArgumentNullException("data");
					}

					this._request.Entity = data; // Data we are sending, serialized when request is made
				}

				// Perform the request
				using (RestResponse<T> response = this._client.Request<T>(this._request))
				{
					// Request is OK
					if (response.StatusCode == HttpStatusCode.OK)
					{
#if !DEBUG
						// Logging enabled?
						if (this._useLogging)
						{
							// Log response
							Log.AddEntry("{0} ({1}) {2}", response.StatusCode, response.Content, resource);
						}
#endif
						// Return the entity (response from server)
						return response.ContentEntity;
					}

					else
					{
						// Not OK, throw an exception
						throw new Exception("RESPONSE: " + response.StatusDescription + ", ERROR: " + response.InnerException.Message);
					}
				}
			}

			catch (Exception ex)
			{
#if DEBUG
                // Write line to debug console
                Debug.WriteLine(ex.Message);
#else
				if (this._useLogging)
				{
					// Log exception
					Log.AddEntry(ex.Message);
				}
#endif
			}

			// Return null, may throw NullReferenceException
			return null; // TODO: Do something else other than return null
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets the client
		/// </summary>
		public RestClient Client
		{
			get { return this._client; }
		}

		/// <summary>
		/// Gets the request
		/// </summary>
		public RestRequest Request
		{
			get { return this._request; }
		}

		/// <summary>
		/// Gets the API username
		/// </summary>
		public string Username
		{
			get { return this._username; }
		}

		/// <summary>
		/// Gets the API password
		/// </summary>
		public string Password
		{
			get { return this._password; }
		}

		#endregion
	}
}